package datas

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/manifoldco/promptui"

	"github.com/c-bata/go-prompt"
	"github.com/c-bata/go-prompt/completer"
)

const END = "<<EOF>>"

var (
	JSON_KEY_RE       = regexp.MustCompile(`".+?"`)
	filePathCompleter = completer.FilePathCompleter{
		IgnoreCase: true,
		Filter: func(fi os.FileInfo) bool {
			return fi.IsDir() || strings.HasSuffix(fi.Name(), ".json")
		},
	}
)

func WriteALl(conn net.Conn, data []byte) (n int, err error) {
	allLen := len(data)
	// n, err = conn.Write(data)
	// if err != nil {
	// 	return
	// }

	// head := make([]byte, 6)
	// head[0] = 'H'
	// head[1] = '5'
	// all := uint32(allLen)
	// fmt.Println("al:", all)
	// binary.BigEndian.PutUint32(head[2:6], all)
	// n, err = conn.Write(head)
	if err != nil {
		log.Println("send head error:", err)
		return
	}
	for n < allLen {
		tn, eerr := conn.Write(data)
		if eerr != nil {
			err = eerr
			return
		}
		n += tn
	}
	conn.Write([]byte(END))
	return
}

func ReadAll(conn net.Conn) ([]byte, error) {
	var err error
	var n int
	buffer := bytes.NewBuffer([]byte{})

	// log.Println("all len:", allLen)

	for {
		buf := make([]byte, 4096)
		wn, werr := conn.Read(buf)
		if bytes.HasSuffix(buf[:wn], []byte(END)) {
			buffer.Write(buf[:wn-len(END)])
			break
		}
		// log.Println("test:", string(buf[:wn]))
		if werr != nil {
			log.Println("read body err:", err)
			return nil, werr
		}
		buffer.Write(buf[:wn])
		err = werr
		n += wn
	}
	return buffer.Bytes(), err

}

func JsonCreateEasy() (m Gi) {
	// var err error
	nowJson := "{}"
	m = make(Gi)
	preparedWords := []prompt.Suggest{}
	filepath.Walk(".", func(path string, file os.FileInfo, err error) error {
		if file.IsDir() {
			return nil
		}
		if strings.Count(path, "/") > 3 {
			return errors.New("more then 3 deep!!!")
		}
		if strings.HasSuffix(path, ".json") || strings.Contains(path, ".json.") {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}
			for _, wd := range JSON_KEY_RE.FindAll(data, -1) {
				preparedWords = append(preparedWords, prompt.Suggest{
					Text:        string(wd),
					Description: "found in:" + path,
				})
			}
		}
		return nil
	})
	// log.Println(preparedWords)
	fmt.Println(`
	Operation:
		"-" to delete
		"+" to load a json
		"" to confirm and exit
	`)
	for {

		res := prompt.Input(fmt.Sprintf("%d >>", len(m)), func(d prompt.Document) []prompt.Suggest {
			return prompt.FilterHasPrefix(preparedWords, d.GetWordBeforeCursor(), true)
		})
		if res == "" {
			o := promptui.Prompt{
				Label:     "confirm use this as json?",
				IsConfirm: true,
			}
			res, err := o.Run()
			if res == "y" && err == nil {
				break
			}
		} else if strings.Contains(res, ":") {

			fieds := strings.SplitN(res, ":", 2)
			key := strings.TrimSpace(fieds[0])
			value := strings.TrimSpace(fieds[1])
			if strings.HasPrefix(key, "\"") {
				key = key[1 : len(key)-1]
			}
			if !strings.HasPrefix(value, "\"") {
				if v, err := strconv.Atoi(value); err == nil {
					m[key] = v
				} else {
					m[key] = value
				}
			} else {

				m[key] = value[1 : len(value)-1]
			}

		} else if strings.Contains(res, " ") {
			fieds := strings.Fields(res)
			if len(fieds) == 2 {
				if v, err := strconv.Atoi(fieds[1]); err == nil {
					m[fieds[0]] = v
				} else {
					m[fieds[0]] = fieds[1]
				}
			}
		} else {
			if res == "-" {
				keys := []string{}
				for k := range m {
					keys = append(keys, k)
				}
				se := promptui.Select{
					Items: keys,
				}
				_, o, err := se.Run()
				if err != nil {
					log.Fatal("err : ", err)
				}
				Locker.Lock()
				delete(m, o)
				Locker.Unlock()
			} else if res == "exit" {
				return
			} else if res == "+" {
				// var s []prompt.Suggest
				filName := prompt.Input("json file load >>", func(d prompt.Document) []prompt.Suggest {
					return filePathCompleter.Complete(d)
				})
				data, err := ioutil.ReadFile(filName)
				if err != nil {
					log.Println("read json file err:", err)
					continue
				}
				err = json.Unmarshal(data, &m)
				if err != nil {
					log.Println("load json file err:", err)
					continue
				}
			} else {
				fmt.Println("must k : \"v ss\" or  k v")
			}
		}
		d, err := json.Marshal(&m)
		if err != nil {
			log.Fatal("json ma err:", err)
		}
		nowJson = string(d)
		fmt.Println(nowJson)
	}
	return
}

func SmartChooseCmdArgs(Executale, BinFilePath string, argsS string, conn net.Conn) (cmdArgs []string) {
	args := append([]string{BinFilePath}, strings.Fields(argsS)...)
	// var cmd *exec.Cmd
	if Executale == "" {
		// cmdArgs = append([]string{"bash", "-c"}, strings.Join(args, " "))
		cmdArgs = args
	} else {
		prefs := func(exe string) []string {
			if exe == "python3" || exe == "python" {
				return strings.Fields(exe)
			} else {
				s, err := Choose(conn, "run by:", "raw", exe)
				if err != nil {
					log.Println("choose runable err:", err)
					return []string{}
				}
				switch s {
				case "raw":
					return []string{}
				default:
					return strings.Fields(exe)
				}
			}

		}(Executale)
		var args2 []string
		if len(prefs) == 0 {

			// cmd = exec.Command(args[0], args[1:]...)
			cmdArgs = args

		} else if len(prefs) > 1 {
			args2 = append([]string{}, prefs[1:]...)
			args2 = append(args2, strings.Join(args, " "))
			// cmd = exec.Command(prefs[0], args2...)
			cmdArgs = append([]string{prefs[0]}, args2...)
		} else {
			args2 = []string{strings.Join(args, " ")}
			cmdArgs = append([]string{prefs[0]}, args2...)
		}
		switch Executale {
		case "python3":
			// pay := strings.Join(cmdArgs, " ")
			// pay = strings.ReplaceAll(pay, "\"", "\\\"")
			cmdArgs = strings.Fields(strings.Join(cmdArgs, " "))
		case "python":

			// pay := strings.Join(cmdArgs, " ")
			// pay = strings.ReplaceAll(pay, "\"", "\\\"")
			// cmdArgs = append([]string{"bash", "-c"}, fmt.Sprintf("\" %s \"", pay))

			cmdArgs = strings.Fields(strings.Join(cmdArgs, " "))
		}
	}
	return
}
